Merged [20921], [20922], [20969], [20971]: Added MySpace service and account for...
[adiumx.git] / Plugins / Purple Service / ESPurpleYahooAccount.m
blob2b21b70e1ad89cd6b534f871d0cf7f5262a57e25
1 /* 
2  * Adium is the legal property of its developers, whose names are listed in the copyright file included
3  * with this source distribution.
4  * 
5  * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
6  * General Public License as published by the Free Software Foundation; either version 2 of the License,
7  * or (at your option) any later version.
8  * 
9  * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
10  * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
11  * Public License for more details.
12  * 
13  * You should have received a copy of the GNU General Public License along with this program; if not,
14  * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
15  */
17 #import <Adium/AIAccountControllerProtocol.h>
18 #import <Adium/AIStatusControllerProtocol.h>
19 #import "ESPurpleYahooAccount.h"
20 #import "ESPurpleYahooAccountViewController.h"
21 #import <AdiumLibpurple/SLPurpleCocoaAdapter.h>
22 #import <Adium/AIHTMLDecoder.h>
23 #import <Adium/AIListContact.h>
24 #import <Adium/AIStatus.h>
25 #import <Adium/ESFileTransfer.h>
26 #import <Libpurple/yahoo.h>
27 #import <Libpurple/yahoo_friend.h>
29 @implementation ESPurpleYahooAccount
31 - (const char*)protocolPlugin
33     return "prpl-yahoo";
36 - (void)configurePurpleAccount
38         [super configurePurpleAccount];
40         purple_account_set_string(account, "room_list_locale", [[self preferenceForKey:KEY_YAHOO_ROOM_LIST_LOCALE
41                                                                                                                                                    group:GROUP_ACCOUNT_STATUS] UTF8String]);
43         //Make sure we're not turning japanese oh no not turning japanese I really think so
44         purple_account_set_bool(account, "yahoojp", FALSE);
47 /*!
48 * @brief The UID will be changed. The account has a chance to perform modifications
49  *
50  * Remove @yahoo.com from the proposedUID - a common mistake is to include it in the yahoo ID
51  *
52  * @param proposedUID The proposed, pre-filtered UID (filtered means it has no characters invalid for this servce)
53  * @result The UID to use; the default implementation just returns proposedUID.
54  */
55 - (NSString *)accountWillSetUID:(NSString *)proposedUID
57         NSString        *correctUID;
58         
59         if ((proposedUID && ([proposedUID length] > 0)) && 
60                 ([proposedUID rangeOfString:@"@yahoo.com" options:NSCaseInsensitiveSearch].location != NSNotFound)) {
61                 correctUID = [proposedUID substringToIndex:[proposedUID rangeOfString:@"@yahoo.com" 
62                                                                                                                                           options:NSCaseInsensitiveSearch].location];
63         } else {
64                 correctUID = proposedUID;
65         }
66         
67         return correctUID;
70 - (NSSet *)supportedPropertyKeys
72         static NSMutableSet *supportedPropertyKeys = nil;
73         
74         if (!supportedPropertyKeys) {
75                 supportedPropertyKeys = [[NSMutableSet alloc] initWithObjects:
76                         @"AvailableMessage",
77                         @"Invisible",
78                         nil];
79                 [supportedPropertyKeys unionSet:[super supportedPropertyKeys]];
80         }
81         
82         return supportedPropertyKeys;
85 /*!
86  * @brief Should set aliases serverside?
87  *
88  * Yahoo supports serverside aliases.
89  */
90 - (BOOL)shouldSetAliasesServerside
92         return YES;
95 #pragma mark Connection
96 - (NSString *)connectionStringForStep:(int)step
98         switch (step)
99         {
100                 case 0:
101                         return AILocalizedString(@"Connecting",nil);
102                         break;
103         }
104         return nil;
107 - (BOOL)shouldAttemptReconnectAfterDisconnectionError:(NSString **)disconnectionError
109         BOOL shouldAttemptReconnect = YES;
110         
111         if (disconnectionError && *disconnectionError) {
112                 if ([*disconnectionError rangeOfString:@"Incorrect password"].location != NSNotFound) {
113                         [self serverReportedInvalidPassword];
114                 } else if (([*disconnectionError rangeOfString:@"You have signed on from another location"].location != NSNotFound) ||
115                                    ([*disconnectionError rangeOfString:@"logged in on a different machine or device"].location != NSNotFound)) {
116                         shouldAttemptReconnect = NO;
117                 }
118         }
119         
120         return shouldAttemptReconnect;
123 #pragma mark Encoding
124 - (NSString *)encodedAttributedString:(NSAttributedString *)inAttributedString forListObject:(AIListObject *)inListObject
125 {       
126         if (inListObject) {
127                 return [AIHTMLDecoder encodeHTML:inAttributedString
128                                                                  headers:NO
129                                                                 fontTags:YES
130                                           includingColorTags:YES
131                                                    closeFontTags:YES
132                                                            styleTags:YES
133                           closeStyleTagsOnFontChange:YES
134                                                   encodeNonASCII:NO
135                                                         encodeSpaces:NO
136                                                           imagesPath:nil
137                                            attachmentsAsText:YES
138                            onlyIncludeOutgoingImages:NO
139                                                   simpleTagsOnly:YES
140                                                   bodyBackground:NO];
141         } else {
142                 return [inAttributedString string];
143         }
146 #pragma mark File transfer
147 - (BOOL)canSendFolders
149         return NO;
152 - (void)beginSendOfFileTransfer:(ESFileTransfer *)fileTransfer
154         [super _beginSendOfFileTransfer:fileTransfer];
158 - (void)acceptFileTransferRequest:(ESFileTransfer *)fileTransfer
160     [super acceptFileTransferRequest:fileTransfer];    
163 - (void)rejectFileReceiveRequest:(ESFileTransfer *)fileTransfer
165     [super rejectFileReceiveRequest:fileTransfer];    
168 - (void)cancelFileTransfer:(ESFileTransfer *)fileTransfer
170         [super cancelFileTransfer:fileTransfer];
173 #pragma mark Status Messages
176  * @brief Status name to use for a Purple buddy
177  */
178 - (NSString *)statusNameForPurpleBuddy:(PurpleBuddy *)buddy
180         NSString                *statusName = nil;
181         PurplePresence  *presence = purple_buddy_get_presence(buddy);
182         PurpleStatus            *status = purple_presence_get_active_status(presence);
183         const char              *purpleStatusID = purple_status_get_id(status);
184         
185         if (!purpleStatusID) return nil;
186         
187         if (!strcmp(purpleStatusID, YAHOO_STATUS_TYPE_BRB)) {
188                 statusName = STATUS_NAME_BRB;
189                 
190         } else if (!strcmp(purpleStatusID, YAHOO_STATUS_TYPE_BUSY)) {
191                 statusName = STATUS_NAME_BUSY;
192                 
193         } else if (!strcmp(purpleStatusID, YAHOO_STATUS_TYPE_NOTATHOME)) {
194                 statusName = STATUS_NAME_NOT_AT_HOME;
195                 
196         } else if (!strcmp(purpleStatusID, YAHOO_STATUS_TYPE_NOTATDESK)) {
197                 statusName = STATUS_NAME_NOT_AT_DESK;
198                 
199         } else if (!strcmp(purpleStatusID, YAHOO_STATUS_TYPE_NOTINOFFICE)) {
200                 statusName = STATUS_NAME_NOT_IN_OFFICE;
201                 
202         } else if (!strcmp(purpleStatusID, YAHOO_STATUS_TYPE_ONPHONE)) {
203                 statusName = STATUS_NAME_PHONE;
204                 
205         } else if (!strcmp(purpleStatusID, YAHOO_STATUS_TYPE_ONVACATION)) {
206                 statusName = STATUS_NAME_VACATION;
207                 
208         } else if (!strcmp(purpleStatusID, YAHOO_STATUS_TYPE_OUTTOLUNCH)) {
209                 statusName = STATUS_NAME_LUNCH;
210                 
211         } else if (!strcmp(purpleStatusID, YAHOO_STATUS_TYPE_STEPPEDOUT)) {
212                 statusName = STATUS_NAME_STEPPED_OUT;
213                 
214         } else if (!strcmp(purpleStatusID, YAHOO_STATUS_TYPE_INVISIBLE)) {
215                 statusName = STATUS_NAME_INVISIBLE;
216         }
217         
218         return statusName;
222  * @brief Status message for a contact
223  */
224 - (NSAttributedString *)statusMessageForPurpleBuddy:(PurpleBuddy *)b
226         NSString                        *statusMessageString = nil;
227         NSAttributedString      *statusMessage = nil;
228         char                            *normalized = g_strdup(purple_normalize(b->account, b->name));
229         struct yahoo_data   *od;
230         YahooFriend                     *f;
231         
232         if ((purple_account_is_connected(account)) &&
233                 (od = account->gc->proto_data) &&
234                 (f = g_hash_table_lookup(od->friends, normalized))) {
235                 
236                 if (f->msg != NULL) {
237                         statusMessageString = [NSString stringWithUTF8String:f->msg];
238                         
239                 } else if (f->status != YAHOO_STATUS_AVAILABLE) {
240                         switch (f->status) {
241                                 case YAHOO_STATUS_BRB:
242                                         statusMessageString = [[adium statusController] localizedDescriptionForCoreStatusName:STATUS_NAME_BRB];
243                                         break;
244                                         
245                                 case YAHOO_STATUS_BUSY:
246                                         statusMessageString = [[adium statusController] localizedDescriptionForCoreStatusName:STATUS_NAME_BUSY];
247                                         break;
248                                         
249                                 case YAHOO_STATUS_NOTATHOME:
250                                         statusMessageString = [[adium statusController] localizedDescriptionForCoreStatusName:STATUS_NAME_NOT_AT_HOME];
251                                         break;
252                                         
253                                 case YAHOO_STATUS_NOTATDESK:
254                                         statusMessageString = [[adium statusController] localizedDescriptionForCoreStatusName:STATUS_NAME_NOT_AT_DESK];
255                                         break;
256                                         
257                                 case YAHOO_STATUS_NOTINOFFICE:
258                                         statusMessageString = [[adium statusController] localizedDescriptionForCoreStatusName:STATUS_NAME_NOT_IN_OFFICE];
259                                         break;
260                                         
261                                 case YAHOO_STATUS_ONPHONE:
262                                         statusMessageString = [[adium statusController] localizedDescriptionForCoreStatusName:STATUS_NAME_PHONE];
263                                         break;
264                                         
265                                 case YAHOO_STATUS_ONVACATION:
266                                         statusMessageString = [[adium statusController] localizedDescriptionForCoreStatusName:STATUS_NAME_VACATION];
267                                         break;
268                                         
269                                 case YAHOO_STATUS_OUTTOLUNCH:
270                                         statusMessageString = [[adium statusController] localizedDescriptionForCoreStatusName:STATUS_NAME_LUNCH];
271                                         break;
272                                         
273                                 case YAHOO_STATUS_STEPPEDOUT:
274                                         statusMessageString = [[adium statusController] localizedDescriptionForCoreStatusName:STATUS_NAME_STEPPED_OUT];
275                                         break;
276                                         
277                                 case YAHOO_STATUS_INVISIBLE:
278                                         statusMessageString = [[adium statusController] localizedDescriptionForCoreStatusName:STATUS_NAME_INVISIBLE];
279                                         //                              statusType = AIInvisibleStatusType; /* Invisible has a special status type */
280                                         break;
281                                         
282                                 case YAHOO_STATUS_AVAILABLE:
283                                 case YAHOO_STATUS_WEBLOGIN:
284                                 case YAHOO_STATUS_CUSTOM:
285                                 case YAHOO_STATUS_IDLE:
286                                 case YAHOO_STATUS_OFFLINE:
287                                 case YAHOO_STATUS_TYPING:
288                                         break;
289                         }
290                 }
291                 
292                 if (statusMessageString) {
293                         statusMessage = [[[NSAttributedString alloc] initWithString:statusMessageString
294                                                                                                                          attributes:nil] autorelease];
295                 }
296         }
298         g_free(normalized);
299         
300         return statusMessage;
304  * @brief Update the status message and away state of the contact
305  */
306 - (void)updateStatusForContact:(AIListContact *)theContact toStatusType:(NSNumber *)statusTypeNumber statusName:(NSString *)statusName statusMessage:(NSAttributedString *)statusMessage
308         NSString                        *statusMessageString = [statusMessage string];
309         char                            *normalized = g_strdup(purple_normalize(account, [[theContact UID] UTF8String]));
310         struct yahoo_data   *od;
311         YahooFriend                     *f;
313         /* Grab the idle time while we have a chance */
314         if ((purple_account_is_connected(account)) &&
315                 (od = account->gc->proto_data) &&
316                 (f = g_hash_table_lookup(od->friends, normalized))) {
318                 if (f->status == YAHOO_STATUS_IDLE) {
319                         //Now idle
320                         int             idle = f->idle;
321                         NSDate  *idleSince;
322                         
323                         if (idle != -1) {
324                                 idleSince = [NSDate dateWithTimeIntervalSinceNow:-idle];
325                         } else {
326                                 idleSince = [NSDate date];
327                         }
328                         
329                         [theContact setStatusObject:idleSince
330                                                                  forKey:@"IdleSince"
331                                                                  notify:NotifyLater];
332                         
333                 } else if (f->status == YAHOO_STATUS_INVISIBLE) {
334                         statusTypeNumber = [NSNumber numberWithInt:AIInvisibleStatusType]; /* Invisible has a special status type */
335                 }
336         }
338         g_free(normalized);
339         
340         //Yahoo doesn't have an explicit mobile state; instead the status message is automatically set to indicate mobility.
341         if (statusMessageString && ([statusMessageString isEqualToString:@"I'm on SMS"] ||
342                                                                 ([statusMessageString rangeOfString:@"I'm mobile"].location != NSNotFound))) {
343                 [theContact setIsMobile:YES notify:NotifyLater];
345         } else if ([theContact isMobile]) {
346                 [theContact setIsMobile:NO notify:NotifyLater];         
347         }
348         
349         [super updateStatusForContact:theContact
350                                          toStatusType:statusTypeNumber
351                                            statusName:statusName
352                                         statusMessage:statusMessage];
356  * @brief Return the purple status ID to be used for a status
358  * Most subclasses should override this method; these generic values may be appropriate for others.
360  * Active services provided nonlocalized status names.  An AIStatus is passed to this method along with a pointer
361  * to the status message.  This method should handle any status whose statusNname this service set as well as any statusName
362  * defined in  AIStatusController.h (which will correspond to the services handled by Adium by default).
363  * It should also handle a status name not specified in either of these places with a sane default, most likely by loooking at
364  * [statusState statusType] for a general idea of the status's type.
366  * @param statusState The status for which to find the purple status ID
367  * @param arguments Prpl-specific arguments which will be passed with the state. Message is handled automatically.
369  * @result The purple status ID
370  */
371 - (const char *)purpleStatusIDForStatus:(AIStatus *)statusState
372                                                         arguments:(NSMutableDictionary *)arguments
374         const char              *statusID = NULL;
375         NSString                *statusName = [statusState statusName];
376         NSString                *statusMessageString = [statusState statusMessageString];
378         if (!statusMessageString) statusMessageString = @"";
380         switch ([statusState statusType]) {
381                 case AIAvailableStatusType:
382                         statusID = YAHOO_STATUS_TYPE_AVAILABLE;
383                         break;
385                 case AIAwayStatusType:
386                 {
387                         if (([statusName isEqualToString:STATUS_NAME_BRB]) ||
388                                 ([statusMessageString caseInsensitiveCompare:[[adium statusController] localizedDescriptionForCoreStatusName:STATUS_NAME_BRB]] == NSOrderedSame))
389                                 statusID = YAHOO_STATUS_TYPE_BRB;
391                         else if (([statusName isEqualToString:STATUS_NAME_BUSY]) ||
392                                          ([statusMessageString caseInsensitiveCompare:[[adium statusController] localizedDescriptionForCoreStatusName:STATUS_NAME_BUSY]] == NSOrderedSame))
393                                 statusID = YAHOO_STATUS_TYPE_BUSY;
395                         else if (([statusName isEqualToString:STATUS_NAME_NOT_AT_HOME]) ||
396                                          ([statusMessageString caseInsensitiveCompare:[[adium statusController] localizedDescriptionForCoreStatusName:STATUS_NAME_NOT_AT_HOME]] == NSOrderedSame))
397                                 statusID = YAHOO_STATUS_TYPE_NOTATHOME;
399                         else if (([statusName isEqualToString:STATUS_NAME_NOT_AT_DESK]) ||
400                                 ([statusMessageString caseInsensitiveCompare:[[adium statusController] localizedDescriptionForCoreStatusName:STATUS_NAME_NOT_AT_DESK]] == NSOrderedSame))
401                                 statusID = YAHOO_STATUS_TYPE_NOTATDESK;
402                         
403                         else if (([statusName isEqualToString:STATUS_NAME_PHONE]) ||
404                                          ([statusMessageString caseInsensitiveCompare:[[adium statusController] localizedDescriptionForCoreStatusName:STATUS_NAME_PHONE]] == NSOrderedSame))
405                                 statusID = YAHOO_STATUS_TYPE_ONPHONE;
406                         
407                         else if (([statusName isEqualToString:STATUS_NAME_VACATION]) ||
408                                          ([statusMessageString caseInsensitiveCompare:[[adium statusController] localizedDescriptionForCoreStatusName:STATUS_NAME_VACATION]] == NSOrderedSame))
409                                 statusID = YAHOO_STATUS_TYPE_ONVACATION;
410                         
411                         else if (([statusName isEqualToString:STATUS_NAME_LUNCH]) ||
412                                          ([statusMessageString caseInsensitiveCompare:[[adium statusController] localizedDescriptionForCoreStatusName:STATUS_NAME_LUNCH]] == NSOrderedSame))
413                                 statusID = YAHOO_STATUS_TYPE_OUTTOLUNCH;
414                         
415                         else if (([statusName isEqualToString:STATUS_NAME_STEPPED_OUT]) ||
416                                          ([statusMessageString caseInsensitiveCompare:[[adium statusController] localizedDescriptionForCoreStatusName:STATUS_NAME_STEPPED_OUT]] == NSOrderedSame))
417                                 statusID = YAHOO_STATUS_TYPE_STEPPEDOUT;
418                         
419                         
420                         break;
421                 }
422                         
423                 case AIInvisibleStatusType:
424                         statusID = YAHOO_STATUS_TYPE_INVISIBLE;
425                         break;
426                 
427                 case AIOfflineStatusType:
428                         break;
429         }
430         
431         //If we didn't get a purple status ID, request one from super
432         if (statusID == NULL) statusID = [super purpleStatusIDForStatus:statusState arguments:arguments];
433         
434         return statusID;
437 - (BOOL)shouldAddMusicalNoteToNowPlayingStatus
439         return NO;
442 #pragma mark Contact List Menu Items
443 - (NSString *)titleForContactMenuLabel:(const char *)label forContact:(AIListContact *)inContact
445         if (!strcmp(label, "Add Buddy")) {
446                 //We handle Add Buddy ourselves
447                 return nil;
448                 
449         } else if (!strcmp(label, "Join in Chat")) {
450                 return [NSString stringWithFormat:AILocalizedString(@"Join %@'s Chat",nil),[inContact formattedUID]];
452         } else if (!strcmp(label, "Initiate Conference")) {
453                 return [NSString stringWithFormat:AILocalizedString(@"Initiate Conference with %@",nil), [inContact formattedUID]];
455         } else if (!strcmp(label, "Presence Settings")) {
456                 return [NSString stringWithFormat:AILocalizedString(@"Presence Settings for %@",nil), [inContact formattedUID]];
458         } else if (!strcmp(label, "Appear Online")) {
459                 return [NSString stringWithFormat:AILocalizedString(@"Appear Online to %@",nil), [inContact formattedUID]];
460                 
461         } else if (!strcmp(label, "Appear Offline")) {
462                 return [NSString stringWithFormat:AILocalizedString(@"Appear Offline to %@",nil), [inContact formattedUID]];
463                 
464         } else if (!strcmp(label, "Appear Permanently Offline")) {
465                 return [NSString stringWithFormat:AILocalizedString(@"Always Appear Offline to %@",nil), [inContact formattedUID]];
466                 
467         } else if (!strcmp(label, "Don't Appear Permanently Offline")) {
468                 return [NSString stringWithFormat:AILocalizedString(@"Don't Always Appear Offline to %@",nil), [inContact formattedUID]];
469                 
470         } else if (!strcmp(label, "View Webcam")) {
471                 //return [NSString stringWithFormat:AILocalizedString(@"View %@'s Webcam",nil), [inContact formattedUID]];              
472                 return nil;
474         } else if (!strcmp(label, "Start Doodling")) {
475                 return nil;
476         }
478         return [super titleForContactMenuLabel:label forContact:inContact];
481 #pragma mark Account Action Menu Items
482 - (NSString *)titleForAccountActionMenuLabel:(const char *)label
484         /* The Yahoo actions are "Activate ID" (or perhaps "Active ID," depending on where in the code you look)
485          * and "Join User in Chat...".  These are dumb. Additionally, Join User in Chat doesn't work as of purple 1.1.4. */
486         return nil;
489 @end